
class OptionType:
    CALL = 'call'
    PUT = 'put'
    ASIAN_CALL = 'asian_call'
    ASIAN_PUT = 'asian_put'

class Payoff:

    base = 360

    def __init__(self, contract):
        self.expiry = float(contract.getAttribute("expiry"))
        option = contract.getElementsByTagName("option")[0]
        self.fixingDays = map(lambda x: int(x.getAttribute("value")), option.getElementsByTagName("fixing"))
        self.fixingDays.append(int(self.expiry*self.base))
        self.fixingDays.sort()
        self.type = option.getAttribute("type")
        if self.type == OptionType.ASIAN_CALL or self.type == OptionType.ASIAN_PUT:          
            self.strikeFixingDays = []
            self.strikeStrategy = option.getAttribute("strike_strategy")
            fixingsInOption =contract.getElementsByTagName("fixings")
            for i in range (0, len(fixingsInOption)):
                if (fixingsInOption[i].getAttribute("value")== option.getAttribute("strike")):
                    self.strikeFixingDays = map(lambda x: int(x.getAttribute("value")), fixingsInOption[i].getElementsByTagName("fixing"))
            if len(self.strikeFixingDays)==0:
                raise Exception('There are not fixings for strike %s ' % option.getAttribute("strike") )          
        else:
            self.strike = float(option.getAttribute("strike"))

    def __repr__(self):
        if (self.type == OptionType.CALL or self.type == OptionType.PUT):
            return "<Payoff expiry:%s type:%s strike:%s>" % (self.expiry, self.type, self.strike)
        elif (self.type == OptionType.ASIAN_CALL or self.type == OptionType.ASIAN_PUT):
            return "<Asian Payoff expiry:%s type:%s Strike Strategy:%s Strike fixings: %s>" % (self.expiry, self.type, self.strikeStrategy, self.strikeFixingDays)

    def thePayoff(self, fixingDaysSpots):
        if self.type == OptionType.CALL:
            return max(fixingDaysSpots.get(self.expiry*self.base) - self.strike, 0)
        elif self.type == OptionType.PUT:
            return max(self.strike - fixingDaysSpots.get(self.expiry*self.base), 0)
        elif self.type == OptionType.ASIAN_CALL:            
            return max(fixingDaysSpots.get(self.expiry*self.base) - self.get_strike_for_strategy(fixingDaysSpots) , 0)
        elif self.type == OptionType.ASIAN_PUT:
            return max(self.get_strike_for_strategy(fixingDaysSpots) - fixingDaysSpots.get(self.expiry*self.base), 0)                
        else:
            raise Exception('optionType must be CALL or PUT')

    def get_strike_for_strategy (self, fixingDaysSpots):
        #fixingDaysSpots will contain a map [DayNumber -> Spot value in that day]
        if self.strikeStrategy == "max":
            return max(map(lambda x: fixingDaysSpots.get(x), self.strikeFixingDays))
        elif self.strikeStrategy == "min":
            return min(map(lambda x: fixingDaysSpots.get(x), self.strikeFixingDays))
        elif self.strikeStrategy == "avg":
            return sum(map(lambda x: fixingDaysSpots.get(x), self.strikeFixingDays))/len(self.strikeFixingDays)       
        else:
            raise Exception('strike_strategy must be max, min or avg')
            